package hcn.sync.util;

/**
 * GenericsUtils
 *
 * @author 9527
 * @date 15/12/9
 */


import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
public class GenericsUtils {

    public static Class getSuperClassGenricType(Class clazz, int index) {
        Type type = clazz.getGenericSuperclass();
        return getActualClass(type, index);
    }

    public static Class getSuperClassGenricType(Class clazz) {
        return getSuperClassGenricType(clazz, 0);
    }

    public static Class getMethodGenericReturnType(Method method, int index) {
        Type returnType = method.getGenericReturnType();
        return getActualClass(returnType, index);
    }

    public static Class getMethodGenericReturnType(Method method) {
        return getMethodGenericReturnType(method, 0);
    }

    public static List<Class> getMethodGenericParameterTypes(Method method, int index) {
        List<Class> results = new ArrayList<Class>();
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        if (index >= genericParameterTypes.length || index < 0) {
            throw new RuntimeException("index " + (index < 0 ? "can not less than 0." : "out of range."));
        }
        Type genericParameterType = genericParameterTypes[index];
        if (genericParameterType instanceof ParameterizedType) {
            ParameterizedType aType = (ParameterizedType) genericParameterType;
            Type[] parameterArgTypes = aType.getActualTypeArguments();
            for (Type parameterArgType : parameterArgTypes) {
                Class parameterArgClass = (Class) parameterArgType;
                results.add(parameterArgClass);
            }
            return results;
        }
        return results;
    }

    public static List<Class> getMethodGenericParameterTypes(Method method) {
        return getMethodGenericParameterTypes(method, 0);
    }

    public static Class getFieldGenericType(Field field, int index) {
        Type genericFieldType = field.getGenericType();
        return getActualClass(genericFieldType, index);
    }

    public static Class getFieldGenericType(Field field) {
        return getFieldGenericType(field, 0);
    }

    private static Class getActualClass(Type type, int index) {
        if (!(type instanceof ParameterizedType)) {
            return Object.class;
        }
        Type[] types = ((ParameterizedType) type).getActualTypeArguments();
        if (index >= types.length || index < 0) {
            throw new RuntimeException("index " + (index < 0 ? "can not less than 0." : "out of range."));
        }
        if (!(types[index] instanceof Class)) {
            return Object.class;
        }
        return (Class) types[index];
    }

}